/******************************************************************************
 *
 * @copyright:          Copyright 2011 UniSky Ltd.
 * @file:
 * @author:              kenping.liu
 * @creation:
 * @description:
 *
 * @modifications
 * date         user                description
 *
 *
 * @more-details
 *
 *****************************************************************************/

#include "u/u_net.h"
#include "u/u_logger.h"
#include "u/u_io.h"

#if defined(WINDOWS)
#include <Ws2spi.h>
#include <Sporder.h>
//#pragma comment(lib, "Sporder.lib")
#endif

U_NAMESPACE_BEGIN(u)
U_NAMESPACE_BEGIN(net)

/////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(WINDOWS)
WinSocketHelper::WinSocketHelper()
{    
    WSADATA wsaData;
    if ( 0!=WSAStartup(MAKEWORD(2,2), &wsaData) )
    {
        u::Log::fatal("[unet] WSAStartup failed: %s", u::get_error_msg());
    }
}
WinSocketHelper::~WinSocketHelper()
{
    WSACleanup();
}
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////
bool Socket::get_host_addr(char(& ip)[64], const char* name)
{
    bool ret = false;
    ZeroMemory(ip, 64);
    if ( INADDR_NONE != inet_addr(name) )
    {
        u_sprintf(ip, "%s", name);
        ret = true;
    }
    else
    {
        hostent* host = ::gethostbyname(name);   //do dns resolvtion
        if ( !host )
        {
            u::Log::info("[unet] get_host_addr: gethostbyname(%s) failed.", name);
        }
        else
        {
            struct in_addr in;
            int iaddr = *(int*)host->h_addr_list[0];
            in.s_addr = (iaddr);

            char *tmp = inet_ntoa(in); //convert ip to string
            if ( tmp )
            {
                u_sprintf(ip, "%s", tmp);
                ret = true;
            }
            else
            {
                u::Log::info("[unet] get_host_addr: inet_ntoa(%s) failed.", name);
            }
        }            
    }
    return ret;
}

void Socket::fill_addr(struct sockaddr_in* sa, const char* ip, int port)
{
    ZeroMemory(sa, sizeof(struct sockaddr_in));
    sa->sin_family = AF_INET;
    sa->sin_addr.s_addr = inet_addr(ip);
    sa->sin_port = htons((USHORT)port);
}

Socket::Socket(bool tcp, const char* name) : _sock(INVALID_SOCKET), _tcp(tcp), _port(0)
{
    set_name(name);
    ZeroMemory(_last_error, 1024);
};

Socket::~Socket()
{ 
    ZeroMemory(_name, 128);
    closex(); 
};

void Socket::set_name(const char* name)
{
    ZeroMemory(_name, 128);
    u_sprintf(_name, "%s", name);
}

SOCKET Socket::createx()
{
    _sock = _tcp? ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) : ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
#if !defined(WINDOWS)
     if (_sock<0)
     {
        _sock = INVALID_SOCKET;
     }
#endif

     if ( INVALID_SOCKET==_sock )
     {
        int err = -1;
#if defined(WINDOWS)
        err = WSAGetLastError();
#endif
        u::Log::info("[unet] Socket[%s:%d]::create failed: %s", _name, _port, u::get_error_msg(err));
     }
     update_port();
     return _sock;
}

void Socket::setoptx(bool is_svr)
{
    // ���Ҫ�Ѿ���������״̬��soket�ڵ���closesocket��ǿ�ƹرգ�������TIME_WAIT�Ĺ�̣�
    //int dont_linger = 0;
    //setsockopt(_sock, SOL_SOCKET, SO_DONTLINGER, (const char*)&dont_linger, sizeof(int));
}

void Socket::update_port()
{
    if ( INVALID_SOCKET!=_sock )
    {
        sockaddr_in addr  = {0};
#if defined(WINDOWS)
        int       namelen = (int)sizeof(sockaddr_in);
#else
        socklen_t namelen = (socklen_t)sizeof(sockaddr_in);
#endif
        if ( !::getsockname(_sock, (sockaddr*)&addr, &namelen) )
        {
            _port = htons(addr.sin_port);
        }
    }
}

int Socket::listenx(int port, const char* svc_addr)
{
    _port = port;
//    u::Log::info("[unet] try to listen %d", _port);
    if ( INVALID_SOCKET!=_sock || INVALID_SOCKET!=createx() )
    {
        _port = port;
        struct sockaddr_in service = {0};
        fill_addr(&service, svc_addr, _port);

    	int on = 1;
    	if ( setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) < 0 ){
    		perror("setsockopt");
    	}

        if ( SOCKET_ERROR!=::bind(_sock, (struct sockaddr*)&service, sizeof(service))
            && (!_tcp || SOCKET_ERROR!=::listen(_sock, 8192)) )
        {
            update_port();
        }
        else
        {
            _port = -1;
            int err = -1;
#if defined(WINDOWS)
            //err = WSAGetLastError();
#endif
            u::Log::info("[unet] Socket[%s:%d]::listen failed: %s", _name, _port, u::get_error_msg(err));
            closex();
        }
    }
    else
    {
        _port = -1;
    }
//    u::Log::trace("[unet] Socket[%s]::listen(%d, '%s')", _name, _port, svc_addr);
    return _port;
}

SOCKET Socket::acceptx()
{
    SOCKET s = INVALID_SOCKET;
    if ( INVALID_SOCKET!=_sock )
    {
        s = ::accept(_sock, NULL, NULL);
        if ( INVALID_SOCKET==s )
        {
            int err = -1;
#if defined(WINDOWS)
            err = WSAGetLastError();
#endif
            u::Log::info("[unet] Socket[%s:%d]::accept failed: %d", _name, _port, err);
        }
    }

    return (s>0 && INVALID_SOCKET!=s)? s : INVALID_SOCKET;
}

bool Socket::connect_timeout(const char* host, int port, struct timeval tv)
{
	bool result = false;
	fd_set fdset;
	if ( INVALID_SOCKET!=_sock || INVALID_SOCKET!=createx() )
	{
        char ip[64] = {0};
        if ( get_host_addr(ip, host) )
        {
            struct sockaddr_in r_addr = {0};
            fill_addr(&r_addr, ip, port);
            fcntl(_sock, F_SETFL, O_NONBLOCK);
            ::connect(_sock, (sockaddr*)&r_addr, sizeof(r_addr));
            FD_ZERO(&fdset);
            FD_SET(_sock, &fdset);
            if (select(_sock + 1, NULL, &fdset, NULL, &tv) == 1)
            {
                int so_error;
                socklen_t len = sizeof so_error;

                getsockopt(_sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
                if (so_error == 0) {
                    update_port();
                    result = true;
                }
            }
        }
	}
	fcntl(_sock, F_SETFL, O_SYNC);

	return result;
}

bool Socket::connectx(const char* host, int port)
{
    bool result = false;
    if ( INVALID_SOCKET!=_sock || INVALID_SOCKET!=createx() )
    {
        setoptx();

        char ip[64] = {0};
        if ( get_host_addr(ip, host) )
        {
            struct sockaddr_in r_addr = {0};
            fill_addr(&r_addr, ip, port);
            int err = ::connect(_sock, (sockaddr*)&r_addr, sizeof(r_addr));
            if ( 0==err )
            {
                update_port();
//                u::Log::trace("[unet] Socket[%s:%d]::connect('%s', %d), successed.", _name, _port, host, port);
                result = true;
            }
            else
            {
#if defined(WINDOWS)
                err = WSAGetLastError();
#endif
//                u::Log::info("[unet] Socket[%s:%d]::connect('%s', %d), failed: %s", _name, _port, host, port, u::get_error_msg(err));
            }
        }
    }
    return result;
}

//////////////////////////////////////////////////////////////////////////////  
// Function Name    : send  
// Description      : try to send all the outbuf  
// Input Parameters : outbuf -- the data which want to be send  
//                    want   -- how many bytes you wan to send  
// Return Value     : If no error occurs, send returns the number of bytes send.   
//                    If the connection has been gracefully closed, the return value is zero.  
///                   Otherwise, a value of SOCKET_ERROR is returned  
//////////////////////////////////////////////////////////////////////////////  
int Socket::sendx(const char* outbuf, UINT want)
{    
    if (INVALID_SOCKET==_sock)
    {
        u::Log::info("[unet] Socket[%s:%d]::send error: Invalid Socket.", _name, _port);
        return -1;
    }
    else if ( NULL==outbuf)
    {
        u::Log::info("[unet] Socket[%s:%d]::send error: NULL buffer.", _name, _port);
        return -1;
    }

    UINT left   = want;
    int      idx    = 0;
    int      result = 0;
    while ( left>0 )
    {
        result = ::send(_sock, &outbuf[idx], left, 0);  
        // send error, or has been close by peer  
        if ( SOCKET_ERROR==result || 0==result )
        {
            //close();
            break;
        }
        left -= result;
        idx += result;
    }
    
    if ( SOCKET_ERROR==result )
    {
        int err = result;
#if defined(WINDOWS)
        err = WSAGetLastError();
#endif
        u::Log::info("[unet] Socket[%s:%d]::send error: %s.", _name, _port, u::get_error_msg(err));
    }

//    u::Log::trace("[unet] socket::send: sent=%d, want=%d", idx, want);
    return 0==left? idx : result;   
}

//////////////////////////////////////////////////////////////////////////////  
// Function Name    : sendto  
// Description      : try to send all the outbuf to the target by UDP
// Input Parameters : target -- destination adress and port
// Input Parameters : outbuf -- the data which want to be send  
//                    want   -- how many bytes you wan to send  
// Return Value     : If no error occurs, sendto returns the number of bytes send.   
//                    If the connection has been gracefully closed, the return value is zero.  
///                   Otherwise, a value of SOCKET_ERROR is returned  
//////////////////////////////////////////////////////////////////////////////  
int Socket::sendtox(const sockaddr_in* target, const char* outbuf, UINT want)
{
    if (INVALID_SOCKET==_sock)
    {
        u::Log::info("[unet] Socket[%s:%d]::sendto error: Invalid Socket.", _name, _port);
        return -1;
    }
    else if ( NULL==outbuf)
    {
        u::Log::info("[unet] Socket[%s:%d]::sendto error: NULL buffer.", _name, _port);
        return -1;
    }

    UINT left   = want;
    int      idx    = 0;
    int      result = 0;
#if defined(WINDOWS)
    int       tolen = (int)sizeof(struct sockaddr_in);
#else
    socklen_t tolen = (socklen_t)sizeof(struct sockaddr_in);
#endif
    while ( left>0 )
    {
        result = ::sendto(_sock, &outbuf[idx], left, 0, (sockaddr *)target, tolen);  
        // send error, or has been close by peer  
        if ( SOCKET_ERROR==result || 0==result )
        {
            //close();
            break;
        }
        left -= result;
        idx += result;
    }

    if ( SOCKET_ERROR==result )
    {
        int err = result;
#if defined(WINDOWS)
        err = WSAGetLastError();
#endif
        u::Log::info("[unet] Socket[%s:%d]::sendto error: %s.", _name, _port, u::get_error_msg(err));
    }

    //u::Log::trace("[unet] socket::send: sent=%d, want=%d", idx, want);
    return 0==left? idx : result;   
}

int Socket::sendtox(const char* dst_ip, unsigned short dst_port, const char* outbuf, UINT want)
{
    struct sockaddr_in target = {0};
    fill_addr(&target, dst_ip, dst_port);
    return sendtox(&target, outbuf, want);
}

//////////////////////////////////////////////////////////////////////////////  
// Function Name     : recv_all
// Description       : try to recv all data we want  
// Input Parameters  : inbuf -- where to store data  
//                     want  -- how much data we want  
// Return Value      : If no error occurs, recv returns the number of bytes received.  
//                     If the connection has been gracefully closed, the return value is zero.  
///                    Otherwise, a value of SOCKET_ERROR is returned  
//////////////////////////////////////////////////////////////////////////////
int Socket::recv_allx(char* inbuf, const int want)
{
    return read_socket_all(inbuf, want);
}

int Socket::recvx(char* inbuf, const int want)
{
    return read_socket(inbuf, want);        
}

int Socket::recvfrom_allx(char* inbuf, const int want, char* src_ip, int& src_port)
{
    struct sockaddr_in src = {0};
    int ret = read_socket_all(inbuf, want, &src);
    src_port = ntohs(src.sin_port);
#if defined(MSC_2008)
    strcpy_s(src_ip, 32, inet_ntoa(src.sin_addr));
#else
    strcpy(src_ip, inet_ntoa(src.sin_addr));
#endif
    return ret;
}

int Socket::recvfromx(char* inbuf, const int want, char* src_ip, int& src_port)
{
    struct sockaddr_in src = {0};
    int ret = read_socket(inbuf, want, &src);
    src_port = ntohs(src.sin_port);
#if defined(MSC_2008)
    strcpy_s(src_ip, 32, inet_ntoa(src.sin_addr));
#else
    strcpy(src_ip, inet_ntoa(src.sin_addr));
#endif
    return ret;
}

int Socket::read_socket(char* inbuf, const int want, struct sockaddr_in* src)
{
    if (INVALID_SOCKET==_sock)
    {
        u::Log::info("[unet] Socket[%s:%d]::recv error: Invalid Socket.", _name, _port);
        return -1;
    }
    else if ( NULL==inbuf)
    {
        u::Log::info("[unet] Socket[%s:%d]::recv error: NULL buffer.", _name, _port);
        return -1;
    }

    int result = 0;
    if (_tcp )
    {
        result = ::recv(_sock, inbuf, want>0? want : 1, 0);
    }
    else
    {
#if defined(WINDOWS)
        int       fromlen = (int)sizeof(struct sockaddr_in);
#else
        socklen_t fromlen = (socklen_t)sizeof(struct sockaddr_in);
#endif
        result = ::recvfrom(_sock, inbuf, want>0? want : 1, 0, (sockaddr *)src, &fromlen);
    }

    if ( SOCKET_ERROR==result )
    {
        int err = result;
#if defined(WINDOWS)
        err = WSAGetLastError();
#endif
        u::Log::info("[unet] Socket[%s:%d]::recv error: %s.", _name, _port, u::get_error_msg(err));
    }
    else if ( 0==result && want>0 )
    {
        u::Log::debug("[unet] Socket[%s:%d]::recv info: The connection has been gracefully closed.", _name, _port);
    }
    return result;
}

int Socket::read_socket_all(char* inbuf, const int want, struct sockaddr_in* src)
{
    if (INVALID_SOCKET==_sock)
    {
        u::Log::info("[unet] Socket[%s:%d]::recv_all error: Invalid Socket.", _name, _port);
        return -1;
    }
    else if ( NULL==inbuf)
    {
        u::Log::info("[unet] Socket[%s:%d]::recv_all error: NULL buffer.", _name, _port);
        return -1;
    }

    int result = 0; 
    int read   = 0;  
#if defined(WINDOWS)
    int       fromlen = (int)sizeof(struct sockaddr_in);
#else
    socklen_t fromlen = (socklen_t)sizeof(struct sockaddr_in);
#endif
    while ( read<want )
    {  
        if (_tcp )
        {
            result = ::recv(_sock, &inbuf[read], want-read, 0);
        }
        else
        {
            result = ::recvfrom(_sock, &inbuf[read], want-read, 0, (sockaddr *)src, &fromlen);
        }

        // send error, or has been close by peer  
        if ( 0==result || SOCKET_ERROR==result )
        {
            break;  
        }
        read += result;  
    }
    if ( SOCKET_ERROR==result )
    {
        int err = result;
#if defined(WINDOWS)
        err = WSAGetLastError();
#endif
        u::Log::info("[unet] Socket[%s:%d]::recv_all error: %s.", _name, _port, u::get_error_msg(err));
    }
//    u::Log::trace("[unet] socket::recv: read=%d, want=%d", read, want);
    // read==want means we do the job successfully
    return read==want? read : result;
}

void Socket::closex()
{
    //u::Log::trace("[unet] Socket[%s]::close.", _name);
    if ( INVALID_SOCKET!=_sock )
    {
#if defined(WINDOWS)
        ::shutdown(_sock, SD_BOTH);
        ::closesocket(_sock);
#else
        ::close(_sock);
#endif
    }
    _sock = INVALID_SOCKET; 
}

///////////////////////////////////////////////////////////////////////////////////
ClientTcpSocket::ClientTcpSocket(SOCKET s) : Socket(true, "tcp-cli-socket")
{
    _sock = s;
    update_port();
}

ClientTcpSocket::~ClientTcpSocket()
{
    closex();
}

///////////////////////////////////////////////////////////////////////////////////
ServerTcpSocket::ServerTcpSocket() : Socket(true, "tcp-svr-socket")
{
}

ServerTcpSocket::~ServerTcpSocket()
{
    closex();
}

ClientTcpSocket* ServerTcpSocket::acceptx()
{
    SOCKET s = Socket::acceptx();
    return (s!=INVALID_SOCKET) ? new ClientTcpSocket(s) : NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UdpSocket::UdpSocket() : Socket(false, "udp-socket")
{
}

UdpSocket::~UdpSocket()
{
    closex();
}


U_NAMESPACE_END(net)
U_NAMESPACE_END(u)
